package main

import (
	"bufio"
	"errors"
	"fmt"

	. "gitee.com/cloudapex/Ghost/utd"

	//"io/ioutil"
	"io"
	"os"
	"strings"
	"time"
)

func MakeProtoGo() error {
	iPaths := ""
	tempFileName := fmt.Sprintf("./%s.%s", FILE_NAME_PROTO, FILE_PROTO_SUFFIX)
	if len(vImportPaths) > 0 {
		iPaths = "-I=" + strings.Join(vImportPaths, " -I=")
	}
	if outStr, err := Sys.ExecCmd("protoc_v3", true, "--go_out=.", tempFileName, "-I=.", iPaths); err != nil {
		return errors.New(fmt.Sprintf("protoc_v3 --go_out=. %s -I=. %s,命令执行失败:\n%s%s\n", tempFileName, iPaths, outStr, err.Error()))
	}

	os.Remove(TargetFile)                                 // 移除老的
	file, _ := os.OpenFile(TargetFile, os.O_CREATE, 0666) // 创建新的
	defer file.Close()

	WriteGoFileHead(file)                        // 写头部
	WriteGoFileMsgEnum(file)                     // 写枚举
	WriteGoFileMsgReg(file)                      // 写注册信息
	if err := WriteGoFileMsg(file); err != nil { // 写消息单元 移除old文件
		return err
	}
	// gofmt
	if outStr, err := Sys.ExecCmd("gofmt", true, "-w", "./"+vOutputsDir); err != nil {
		return errors.New(fmt.Sprintf("gofmt -w %s, 命令执行失败:\n%s%v\n", vOutputsDir, outStr, err))
	}

	os.Remove("./" + FILE_NAME_PROTO + FILE_PB_PROTO_SUFFIX) //删除.pb.go文件

	// go install
	if outStr, err := Sys.ExecCmd("go", true, "build"); err != nil {
		return errors.New(fmt.Sprintf("go install %s, 命令执行失败:\n%s%v\n", vOutputsDir, outStr, err))
	}
	return nil
}

//============================生成一条注册信息 写入注册信息=======================
// var strGoFomatReg = `	RegMsgMakeHandles(%s,		Marshal%s,		Unmarshal%s)
// `
var strGoFomatReg = `	ntc.RegistMessage(%s,	"%s",	func() ntc.IMsger { return Make%s() })
`

func MakeOneGoRegMsg(msgName string) string {
	msgType := MakeOneMsgType(msgName)
	return fmt.Sprintf(strGoFomatReg, msgType, msgType, msgName)
}

/////////////////////////////////////////////////////////////////////////////
// 							写文件头部
/////////////////////////////////////////////////////////////////////////////
var strHead = `package %s

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description:协议包的最终文件[提供默认构造函数|序列化/反序列化|自动注册]
# !!!文件自动生成，不要手动修改!!!
*******************************************************************************/
import (
	%sproto "gitee.com/cloudapex/protobuf/proto"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal

`

func WriteGoFileHead(file *os.File) {
	imports := ""
	for _, val := range vImportPacks {
		imports += "\"" + val + "\"\n\t"
	}
	str := fmt.Sprintf(strHead, vPacketName, time.Now().Format("2006-01-02"), imports)
	file.WriteString(str)
}

/////////////////////////////////////////////////////////////////////////////
// 							写消息类型枚举部分
/////////////////////////////////////////////////////////////////////////////
func WriteGoFileMsgEnum(file *os.File) {

	str := `const (
`
	first := true
	for _, value := range MsgTypeLinesAll {

		value = strings.Replace(value, "=", "= iota +", 1)
		value = strings.Replace(value, ",", " ", 1)

		if strings.Contains(value, "iota") {
			if first {
				first = false
			} else {
				file.WriteString(")\n")
			}
			file.WriteString(str)
		}
		file.WriteString("	" + value + "\n")
	}
	file.WriteString(")\n")
}

/////////////////////////////////////////////////////////////////////////////
// 							写消息注册部分
/////////////////////////////////////////////////////////////////////////////
func WriteGoFileMsgReg(file *os.File) {
	str := `
func init() {
`
	file.WriteString(str)

	for _, value := range MsgStructNamesAll {
		file.WriteString(MakeOneGoRegMsg(value))
	}

	file.WriteString("}\n")
}

/////////////////////////////////////////////////////////////////////////////
// 							写消息单元部分
/////////////////////////////////////////////////////////////////////////////

//==========修正proto.go文件==============
var strMakeGoFomat = `func (m *%s) SetHead(head *ntc.MsgHead){ m.Head = head }
func (m *%s) MsgType() string { return m.Head.GetType() }
func (m *%s) MsgName() string { return m.Head.Name() }
func Make%s() *%s {
	return &%s{Head: &ntc.MsgHead{Types: uint32(%s << 16)}}
}
`

func WriteGoFileMsg(file *os.File) error {
	// 打开 ./Protocols.pb.go
	f, err := os.Open("./" + FILE_NAME_PROTO + FILE_PB_PROTO_SUFFIX)
	if err != nil {
		return err
	}
	defer f.Close()

	bNeed, bNeedEnum, bNeedEnumField := false, false, false
	msgNameO, mapMsgEnumName := "", make(map[string]string)
	shoudEnd := false
	// 按行读取
	buf := bufio.NewReader(f)
	for {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				line += "\n"
				file.WriteString(line)
				return nil
			}
			return err
		}

		// 空行过滤
		if strings.TrimSpace(line) == "" {
			continue
		}
		// 描述符不要
		if strings.Contains(line, "EnumDescriptor() ([]byte, []int)") {
			continue
		}
		// 描述符不要
		if strings.Contains(line, "Descriptor() ([]byte, []int)") {
			continue
		}
		//"var _ = proto.Marshal"
		if !bNeed && strings.HasPrefix(line, "type ") { // type 打头 开始写入
			bNeed = true // 需要写入
		}
		// 只写入必要的:[优化GetHead函数|添加构造函数|删除多余的方法|枚举的转换规则替换]
		if bNeed {
			if strings.Contains(line, "GetHead() *ntc.MsgHead {") { // man struct end
				fields := strings.Split(line, "*")
				name := strings.Split(fields[1], ")")
				file.WriteString(strings.TrimRight(line, "\r\n") + " return m.Head }\n") // GetHead(){...} 修改为一行
				file.WriteString(fmt.Sprintf(strMakeGoFomat, name[0], name[0],name[0], name[0], name[0], name[0], MakeOneMsgType(name[0])))
				bNeed, shoudEnd = false, false // 一条消息结束则标记重置
			} else if strings.Contains(line, "() *") { // sub struct end
				bNeed, shoudEnd = false, false // 一条结构结束则标记重置(删掉哪些方法)
			} else if strings.Contains(line, ") ProtoMessage()") {
				shoudEnd = true
			} else if shoudEnd {
				bNeed, shoudEnd = false, false
			}
			// 修改
			if bNeed { // Replace
				// 先解析消息名
				if strings.HasPrefix(line, "type Msg") {
					fields := strings.Fields(line)
					fields = strings.Split(fields[1], "_")
					msgNameO = fields[0]
					mapMsgEnumName[msgNameO] = ModifyMsgEnumName2(msgNameO)
					if strings.Contains(line, " int32") { // enum  eg:type MsgAllocIdA_AError int32
						bNeedEnum = true // 检测是枚举
					} else if strings.Contains(line, " struct {") { // msg
						bNeedEnumField = true // 某个字段是枚举类型的
					}
				} else if strings.Contains(line, "return proto.EnumName(Msg") {
					// 遇到枚举结束
					bNeedEnum, bNeed = false, false
					line = strings.Replace(line, msgNameO+"_", mapMsgEnumName[msgNameO], 2)
					if strings.HasSuffix(line, "}") {
						file.WriteString(line)
					} else {
						file.WriteString(line + "}\n")
					}
					continue
				} else if strings.TrimSpace(line) == "}" { // 遇到结束符就不再修改 结构内枚举字段
					bNeedEnumField = false
					line = "} //\n"
				}

				// 需要修改枚举部分
				if bNeedEnum {
					line = strings.Replace(line, msgNameO+"_", mapMsgEnumName[msgNameO], 2)
				}
				// 需要修改 结构中的 枚举字段
				if bNeedEnumField {
					if strings.Contains(line, "enum=") {
						line = strings.Replace(line, msgNameO+"_", mapMsgEnumName[msgNameO], 2)
					}
				}
				// 写入
				file.WriteString(line)
			}
		}
	}
}
